use futures::{
    executor::block_on,
    future::FutureExt,
    pin_mut,
    select,
    join,
    StreamExt
};

use async_std::{
    task,
    io,
    prelude::*,
};

use std::time::Duration;

#[test]
fn test_task_block() {
    block_on(async {
        println!("Hello, world!");
    })
}

async fn learn_dance() {
    for i in 1..11 {
        println!("learn dance.{}", i);
        task::sleep(Duration::from_secs(1)).await
    }
}

async fn dance() -> bool {
    learn_dance().await;
    println!("start dance.");
    return true
}

async fn song() {
    println!("start song.");
}

#[test]
fn test_join(){
    block_on(async {
        join!(dance(), song())
    });
}

#[test]
fn test_select() {
    println!("start");

    task::spawn(async {
        let dance = dance().fuse();
        let song = song().fuse();
        let timeout = task::sleep(Duration::from_secs(1)).fuse();

        pin_mut!(dance, song, timeout);

        loop {
            select! {
                (x) = dance => {
                    println!("select dance done = {}", x);
                },
                () = song => {
                    println!("select song");
                },
                () = timeout => {
                    // 模拟强行结束select任务组
                    println!("timeout");
                    break;
                },
                default => {},
                complete => break,
            }
        }
    });

    println!("block");

    // 保证所有任务执行完
    block_on(task::sleep(Duration::from_secs(30)));
}

// 打印计数模拟延时任务
async fn async_print(num:i32) {
    println!("num 0-{} -> start", num);
    for i in 1..1+num {
        println!("num 0-{} -> {}", num, i);
        task::sleep(Duration::from_millis(500)).await;
    }
    println!("num 0-{} -> done", num);
}

// 从stdin中取出数字，并异步执行task
async fn async_read_stdin() {
    let stdin = io::BufReader::new(io::stdin());
    let mut line_from_stdin = StreamExt::fuse(stdin.lines());
    loop {
        select! {
            line = line_from_stdin.next().fuse() => {
                if let Some(Ok(line)) = line {
                    if let Ok(num) = line.parse() {
                        task::spawn(async_print(num));
                    }
                }
            }
        }
    }
}

#[test]
fn test_stdin(){
    block_on(async_read_stdin());
}
